﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.IO;
using System.Threading;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
using MTS;

namespace MTSCLI
{
    public class Surveyor
    {
        BackgroundWorker RenderWorker;
        BlockColors BlockTool = new BlockColors();

        string n = Environment.NewLine;

        WorldRenderer Renderer = new WorldRenderer();
        

        private AutoResetEvent ResetEvent = new AutoResetEvent(false);

        private string OutputDirectory;
        private string OutputSuffix;
        private bool UseSuffix = false;
        private ImageType CurrentMode;

        private string LastState;

        public void Render(List<string> Worlds, List<RenderSettings> RenderTasks, string OutDir, string CacheDir = null)
        {

            OutputDirectory = OutDir;

            UseSuffix = (Worlds.Count > 1);

            RenderWorker = new BackgroundWorker();
            RenderWorker.WorkerSupportsCancellation = false;
            RenderWorker.WorkerReportsProgress = true;
            RenderWorker.DoWork += new DoWorkEventHandler(RendererWork);
            RenderWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(RendererComplete);
            RenderWorker.ProgressChanged += new ProgressChangedEventHandler(RendererProgress);

            foreach (string WorldPath in Worlds)
            {
                Program.WriteLineColored(WorldPath + ":", ConsoleColor.DarkBlue, ConsoleColor.White);

                OutputSuffix = WorldPath.Substring(WorldPath.LastIndexOf('\\') + 1);

                foreach (RenderSettings t in RenderTasks)
                {
                    if (!String.IsNullOrEmpty(CacheDir))
                        if (Directory.Exists(CacheDir))
                            t.CacheDir = CacheDir;
                    ResetEvent = new AutoResetEvent(false);
                    CurrentMode = t.TileType;

                    t.WorldPath = WorldPath;

                    switch (t.TileType)
                    {
                        case ImageType.TERRAIN:
                            Program.WriteColored("Terrain map:", ConsoleColor.DarkYellow, ConsoleColor.White);
                            StartRender(t);
                            ResetEvent.WaitOne();
                            break;
                        case ImageType.OBLIQUE:
                            Program.WriteColored("Oblique map, brightness " + (int)t.LightingLevel + ":", ConsoleColor.DarkYellow, ConsoleColor.White);
                            StartRender(t);
                            ResetEvent.WaitOne();
                            break;
                        case ImageType.CAVEMAP:
                            Program.WriteColored("Cave map:", ConsoleColor.DarkYellow, ConsoleColor.White);
                            StartRender(t);
                            ResetEvent.WaitOne();
                            break;
                        case ImageType.HEIGHTMAP:
                            Program.WriteColored("Height map:", ConsoleColor.DarkYellow, ConsoleColor.White);
                            StartRender(t);
                            ResetEvent.WaitOne();
                            break;
                        case ImageType.SPECTROGRAPH:
                            Program.WriteColored("Spectrograph (" + (int)t.SpectralBlock + ", " + BlockTool.Blocks[(int)t.SpectralBlock].Name + "):", ConsoleColor.DarkYellow, ConsoleColor.White);
                            StartRender(t);
                            ResetEvent.WaitOne();
                            break;
                        default:
                            Console.WriteLine("Unknown or unsupported task \"" + t.TileType.ToString() + "\"!");
                            break;
                    }
                }
                Console.WriteLine();
            }

            WorkComplete("Work complete!" + n + "Output directory: " + OutDir);
        }

        void StartRender(RenderSettings Settings)
        {
            RenderWorker.RunWorkerAsync(Settings);
        }

        void RendererProgress(object sender, ProgressChangedEventArgs e)
        {
#if DEBUG
            if (e.UserState.ToString() != LastState)
            {
                LastState = e.UserState.ToString();
                Console.WriteLine(LastState);
            }
#endif
        }

        void RendererComplete(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                Program.WriteLineColored("Failed: " + e.Error.Message, ConsoleColor.DarkRed, ConsoleColor.White);
            }
            else if (e.Cancelled)
            {
                Program.WriteLineColored("Aborted.", ConsoleColor.DarkRed, ConsoleColor.White);
            }
            else
            {
                BitmapSource output = (BitmapSource)e.Result;

                string OutPath = Path.GetFullPath(OutputDirectory);
                if (UseSuffix)
                    OutPath = Path.Combine(OutPath, OutputSuffix);
                if (!Directory.Exists(OutPath))
                    Directory.CreateDirectory(OutPath);

                OutPath = Path.Combine(OutPath, CurrentMode.ToString() + ".png");

                FileStream stream = new FileStream(OutPath, FileMode.Create);
                BitmapEncoder encoder = new PngBitmapEncoder();

                encoder.Frames.Add(BitmapFrame.Create(output));
                encoder.Save(stream);
                stream.Close();

                Program.WriteLineColored("Complete!", ConsoleColor.DarkGreen, ConsoleColor.White);
            }
            ResetEvent.Set();
        }

        void RendererWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            RenderSettings RS = (RenderSettings)e.Argument;
            e.Result = Renderer.renderWorld(RS, worker);
        }

        public delegate void WorkCompleteHandler(string Message);
        public event WorkCompleteHandler WorkComplete;
    }
}
